﻿using NUnit.Framework;
using Shouldly;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;

namespace Test.MySql.ColumnTypeTests
{
    [TestFixture]
    internal class ColumnTypesTestEnumSets : TestBase
    {
        #region 模型
        [Table("test")]
        public class Model
        {
            /// <summary>
            /// column: t_enum enum('LiuBei','GuanYu','ZhangFei')
            /// </summary>
            public Enum1? t_enum { get; set; }
            /// <summary>
            /// column: t_set set('LiuBei_set','GuanYu_set','ZhangFei_set')
            /// </summary>
            public Enum2? t_set { get; set; }
        }
        public enum Enum1
        {
            LiuBei = 1,
            GuanYu = 2,
            ZhangFei = 3,
        }
        [Flags]
        public enum Enum2
        {
            LiuBei_set = 1,
            GuanYu_set = 2,
            ZhangFei_set = 4,
        }

        [Table("test")]
        public class ModelDbInt
        {
            /// <summary>
            /// column: t_enum int
            /// </summary>
            [Column(TypeName = "int")]
            public Enum1? t_enum { get; set; }
            /// <summary>
            /// column: t_set int
            /// </summary>
            [Column(TypeName = "int")]
            public Enum2? t_set { get; set; }
        }

        [Table("test")]
        public class ModelDbString
        {
            /// <summary>
            /// column: t_enum varchar(200)
            /// </summary>
            [Column(TypeName = "varchar(200)")]
            public Enum1? t_enum { get; set; }
            /// <summary>
            /// column: t_set varchar(200)
            /// </summary>
            [Column(TypeName = "varchar(200)")]
            public Enum2? t_set { get; set; }
        }
        #endregion

        [SetUp]
        public void Setup()
        {
            DropTableIfExist("test");
            db.ExecuteSql(@"
create table test(
	t_enum enum('LiuBei','GuanYu','ZhangFei'),
    t_set set('LiuBei_set','GuanYu_set','ZhangFei_set')
)");
            db.ExecuteSql("""
                insert into test.test(t_enum,t_set)	values (1,5),(2,3),(3,0);
                """);
        }

        [Test]
        public void TestDefaultParse()
        {
            /*
             * mysql中 enum 是从1开始,而c#是从0开始, 所以最好c#定义的枚举也从1开始
             *  如果mysql是严格模式,那么是无法将0插入到mysql中的
             */

            var dicList = db.SelectDictionaryList("select * from test");
            var dic = dicList[0];
            dic["t_enum"].GetType().ShouldBe(typeof(string));
            dic["t_set"].GetType().ShouldBe(typeof(string));
            dic["t_enum"].ShouldBe("LiuBei");
            dic["t_set"].ShouldBe("LiuBei_set,ZhangFei_set");

            dic = dicList[2];
            dic["t_enum"].GetType().ShouldBe(typeof(string));
            dic["t_set"].GetType().ShouldBe(typeof(string));
            dic["t_enum"].ShouldBe("ZhangFei");
            dic["t_set"].ShouldBe("");
        }

        [Test]
        public void TestOrmReadWrite()
        {
            var list = db.SelectModelList<Model>("select * from test");
            validate(list);

            TruncateTable("test");

            var sql = db.Insert<Model>().SetEntity(list).ToSql();
            sql.ShouldBe("""
                insert into test(`t_enum`,`t_set`) values
                    (1,5),
                    (2,3),
                    (3,0);
                """);
            db.Insert<Model>().SetEntity(list).ExecuteAffrows();
            list = db.SelectModelList<Model>("select * from test");
            validate(list);
        }
        private void validate(List<Model> list)
        {
            var model = list[0];
            model.t_enum.ShouldBe(Enum1.LiuBei);
            model.t_set.ShouldBe(Enum2.LiuBei_set | Enum2.ZhangFei_set);

            model = list[1];
            model.t_enum.ShouldBe(Enum1.GuanYu);
            model.t_set.ShouldBe(Enum2.LiuBei_set | Enum2.GuanYu_set);

            model = list[2];
            model.t_enum.ShouldBe(Enum1.ZhangFei);
            model.t_set.ShouldBe((Enum2)0);
        }
        private void validate(List<ModelDbInt> list)
        {
            var model = list[0];
            model.t_enum.ShouldBe(Enum1.LiuBei);
            model.t_set.ShouldBe(Enum2.LiuBei_set | Enum2.ZhangFei_set);

            model = list[1];
            model.t_enum.ShouldBe(Enum1.GuanYu);
            model.t_set.ShouldBe(Enum2.LiuBei_set | Enum2.GuanYu_set);

            model = list[2];
            model.t_enum.ShouldBe(Enum1.ZhangFei);
            model.t_set.ShouldBe((Enum2)0);
        }
        private void validate(List<ModelDbString> list)
        {
            var model = list[0];
            model.t_enum.ShouldBe(Enum1.LiuBei);
            model.t_set.ShouldBe(Enum2.LiuBei_set | Enum2.ZhangFei_set);

            model = list[1];
            model.t_enum.ShouldBe(Enum1.GuanYu);
            model.t_set.ShouldBe(Enum2.LiuBei_set | Enum2.GuanYu_set);

            model = list[2];
            model.t_enum.ShouldBe(Enum1.ZhangFei);
            model.t_set.ShouldBe((Enum2)0);
        }

        [Test]
        public void TestOrmReadWriteDbInt()
        {
            DropTableIfExist("test");
            db.ExecuteSql(@"
create table test(
	t_enum int,
    t_set int
)");
            db.ExecuteSql("""
                insert into test.test(t_enum,t_set)	values (1,5),(2,3),(3,0);
                """);

            var list = db.SelectModelList<ModelDbInt>("select * from test");
            validate(list);
            TruncateTable("test");

            //写入
            var sql = db.Insert<ModelDbInt>().SetEntity(list).ToSql();
            sql.ShouldBe("""
                insert into test(`t_enum`,`t_set`) values
                    (1,5),
                    (2,3),
                    (3,0);
                """);
            db.Insert<ModelDbInt>().SetEntity(list).ExecuteAffrows();
            list = db.SelectModelList<ModelDbInt>("select * from test");
            validate(list);
        }

        [Test]
        public void TestOrmReadWriteDbString()
        {
            //c#中是枚举, db中是string 读取写入的时候要兼容不从1开始的枚举(主要是读取)
            DropTableIfExist("test");
            db.ExecuteSql(@"
create table test(
	t_enum varchar(200),
    t_set varchar(200)
)");
            db.ExecuteSql("""
                insert into test.test(t_enum,t_set)	values 
                    ('LiuBei','LiuBei_set, ZhangFei_set'),
                    ('GuanYu','LiuBei_set,GuanYu_set'),
                    ('ZhangFei','');
                """);

            var list = db.SelectModelList<ModelDbString>("select * from test");
            validate(list);
            TruncateTable("test");

            //写入
            var sql = db.Insert<ModelDbString>().SetEntity(list).ToSql();
            //直接将 '0' 写入, 因为也不知道写其他什么值, 比报错好点(对于这种不规矩的用法)
            sql.ShouldBe("""
                insert into test(`t_enum`,`t_set`) values
                    ('LiuBei','LiuBei_set, ZhangFei_set'),
                    ('GuanYu','LiuBei_set, GuanYu_set'),
                    ('ZhangFei','0');
                """);
            db.Insert<ModelDbString>().SetEntity(list).ExecuteAffrows();
            list = db.SelectModelList<ModelDbString>("select * from test");
            validate(list);
        }
    }
}
